﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Mozlite.Properties;

namespace Mozlite.Cryptography
{
    /// <summary>
    /// 双向加密算法。
    /// </summary>
    public class SymmetricAlgorithm
    {
        /// <summary>
        /// 初始化加密算法。
        /// </summary>
        /// <param name="algorithm">加密算法枚举。</param>
        public SymmetricAlgorithm(EncryptionAlgorithm algorithm) {
            this.Algorithm = algorithm;
            switch (algorithm)
            {
                case EncryptionAlgorithm.Des:
                        this.cryptor = new DESCryptoServiceProvider();
                        this.IVSize = 8;
                        break;
                case EncryptionAlgorithm.TripleDes:
                        this.cryptor = new TripleDESCryptoServiceProvider();
                        this.IVSize = 8;
                        break;
                case EncryptionAlgorithm.Rc2:
                        this.cryptor = new RC2CryptoServiceProvider();
                        this.IVSize = 8;
                        break;
                case EncryptionAlgorithm.Rijndael:
                        this.cryptor = new RijndaelManaged();
                        this.IVSize = 16;
                        break;
                default:
                    {
                        throw new CryptographicException(Resources.EncryptionAlgorithmNotSupported.FormatWith(algorithm));
                    }
            }
            this.cryptor.Mode = CipherMode.CBC;
            KeySize = this.cryptor.KeySize / 8;
        }

        private System.Security.Cryptography.SymmetricAlgorithm cryptor;

        /// <summary>
        /// 算法枚举。
        /// </summary>
        public EncryptionAlgorithm Algorithm { get; private set; }

        /// <summary>
        /// 密钥大小。
        /// </summary>
        public int KeySize { get; private set; }

        internal int IVSize { get; private set; }

        /// <summary>
        /// 加密数据。
        /// </summary>
        /// <param name="data">需要加密的数据。</param>
        /// <param name="key">加密钥匙。</param>
        /// <returns>返回加密后的数据。</returns>
        public byte[] Encrypt(byte[] data, byte[] key)
        {
            MemoryStream ms = new MemoryStream();
            cryptor.IV = IV;
            if(key!=null)
                Key = key;
            ICryptoTransform transform = this.cryptor.CreateEncryptor();
            using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Write))
            {
                cs.Write(data, 0, data.Length);
                cs.FlushFinalBlock();
                cs.Close();
            }
            return ms.ToArray();
        }

        /// <summary>
        /// 解密数据。
        /// </summary>
        /// <param name="data">需要解密的数据。</param>
        /// <param name="key">解密钥匙。</param>
        /// <returns>返回解密后的数据。</returns>
        public byte[] Decrypt(byte[] data, byte[] key)
        {
            MemoryStream ms = new MemoryStream();
            Key = key;
            ICryptoTransform transform = this.cryptor.CreateDecryptor();
            using (CryptoStream cs = new CryptoStream(ms, transform, CryptoStreamMode.Write))
            {
                cs.Write(data, 0, data.Length);
                cs.FlushFinalBlock();
                cs.Close();
            }
            return ms.ToArray();
        } 

        /// <summary>
        /// 获取或设置初始化向量。
        /// </summary>
        public byte[] IV { get { return this.cryptor.IV; } set { this.cryptor.IV = value; } }

        /// <summary>
        /// 获取密钥。
        /// </summary>
        public byte[] Key { get { return this.cryptor.Key; } private set { this.cryptor.Key = value; } }

        /// <summary>
        /// 生成密钥。
        /// </summary>
        /// <param name="algorithm">加密方法枚举类。</param>
        /// <returns>如果是双向加密方法，则返回相应的密钥，否则返回<c>null</c>。</returns>
        public static byte[] GenerateKey(EncryptionAlgorithm algorithm)
        {
            var symmetric = new SymmetricAlgorithm(algorithm);
            return symmetric.Key;
        }
    }
}
